// MailNoter - A capture tool for emails

// Copyright (C) 2009, 2014 - MailNoter

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//

#include "stdafx.h"
#include "MailMsg.h"

CMailMsg::CMailMsg()
    : m_lpMapiLogon(NULL)
    , m_lpMapiSendMail(NULL)
    , m_lpMapiLogoff(NULL)
    , m_lpMapiResolveName(NULL)
    , m_lpMapiFreeBuffer(NULL)
    , m_bReady(FALSE)
    , m_bHTML(FALSE)
    , m_hMapi(NULL)
{
}

CMailMsg::~CMailMsg()
{
    if (m_bReady)
        Uninitialize();
}

CMailMsg& CMailMsg::SetFrom(std::string sAddress, std::string sName)
{
    if (m_bReady || Initialize())
    {
        // only one sender allowed
        if (!m_from.empty())
            m_from.clear();

        m_from.push_back(TStrStrPair(sAddress,sName));
    }

    return *this;
}

CMailMsg& CMailMsg::SetTo(std::string sAddress, std::string sName)
{
    if (m_bReady || Initialize())
    {
        // only one recipient allowed
        if (!m_to.empty())
            m_to.clear();

        m_to.push_back(TStrStrPair(sAddress,sName));
    }

    return *this;
}

CMailMsg& CMailMsg::SetCc(std::string sAddress, std::string sName)
{
    if (m_bReady || Initialize())
    {
        m_cc.push_back(TStrStrPair(sAddress,sName));
    }

    return *this;
}

CMailMsg& CMailMsg::SetBc(std::string sAddress, std::string sName)
{
    if (m_bReady || Initialize())
    {
        m_bcc.push_back(TStrStrPair(sAddress, sName));
    }

    return *this;
}

CMailMsg& CMailMsg::AddAttachment(std::string sAttachment, std::string sTitle)
{
    if (m_bReady || Initialize())
    {
        m_attachments.push_back(TStrStrPair(sAttachment, sTitle));
    }

    return *this;
}

BOOL CMailMsg::Send()
{
    int status = MAPISend();
    if (status != 0)
        return status == 1;

    return FALSE;
}

int CMailMsg::cResolveName(LHANDLE m_lhSession, const char * lpszName, lpMapiRecipDesc *ppRecip)
{
    HRESULT hRes = E_FAIL;
    FLAGS flFlags = 0L;
    lpMapiRecipDesc pRecips = NULL;

    // Always check to make sure there is an active session
    if (m_lhSession)
    {
        ULONG ulReserved = 0L;

        hRes = m_lpMapiResolveName(
            m_lhSession,                        // Session handle
            0L,                                 // Parent window.
            const_cast<LPSTR>(lpszName),        // Name of recipient.  Passed in by argv.
            flFlags,                            // Flags set to 0 for MAPIResolveName.
            ulReserved,
            &pRecips
            );

        if (hRes == SUCCESS_SUCCESS)
        {
            // Copy the recipient descriptor returned from MAPIResolveName to
            // the out parameter for this function,
            *ppRecip = pRecips;
        }
    }
    return hRes;
}

int CMailMsg::MAPISend(bool bResolveNames /* = false */)
{

    TStrStrVector::iterator p;
    MapiRecipDesc *         pRecipients = NULL;
    MapiRecipDesc *         pOriginator = NULL;
    MapiRecipDesc *         pFirstRecipient = NULL;
    MapiFileDesc *          pAttachments = NULL;
    ULONG                   status = 0;
    MapiMessage             message;
    std::vector<MapiRecipDesc*> buffersToFree;
    MapiRecipDesc *         pRecip;
    MapiRecipDesc           grecip;

    if (m_bReady || Initialize())
    {
        LHANDLE hMapiSession;
        int     nIndex = 0;
        size_t  nRecipients = 0;
        ULONG   nAttachments = 0;
        status = m_lpMapiLogon(NULL, NULL, NULL, MAPI_NEW_SESSION | MAPI_LOGON_UI, 0, &hMapiSession);

        if (status != SUCCESS_SUCCESS && status != MAPI_E_USER_ABORT)
        {
            //Failed to create a create mapi session, try to acquire a shared mapi session
            status = m_lpMapiLogon(NULL, NULL, NULL, 0, 0, &hMapiSession);
            if (status != SUCCESS_SUCCESS)
            {
                return FALSE;
            }
        }

        nRecipients = m_to.size() + m_cc.size() + m_bcc.size() + m_from.size();
        if (nRecipients)
        {
            pRecipients = new MapiRecipDesc[nRecipients];
            SecureZeroMemory(pRecipients, nRecipients * sizeof(MapiRecipDesc));
        }

        nAttachments = (ULONG)m_attachments.size();
        if ((m_bHTML) && (!m_sMessage.empty()))
            nAttachments++;
        if (nAttachments)
            pAttachments = new MapiFileDesc[nAttachments];

        if (pRecipients)
        {
            pFirstRecipient = pRecipients;
            if (!m_from.empty())
            {
                // set from
                if ((bResolveNames)&&(cResolveName(hMapiSession, m_from.begin()->first.c_str(), &pOriginator) == SUCCESS_SUCCESS))
                {
                    buffersToFree.push_back(pOriginator);
                }
            }
            if (!m_to.empty())
            {
                if ((bResolveNames)&&(cResolveName(hMapiSession, m_to.begin()->first.c_str(), &pRecip) == SUCCESS_SUCCESS))
                {
                    if (pFirstRecipient == NULL)
                        pFirstRecipient = &pRecipients[nIndex];
                    pRecip->ulRecipClass = MAPI_TO;
                    memcpy(&pRecipients[nIndex], pRecip, sizeof(pRecipients[nIndex]));
                    buffersToFree.push_back(pRecip);
                    nIndex++;
                }
                else
                {
                    if (pFirstRecipient == NULL)
                        pFirstRecipient = &pRecipients[nIndex];
                    grecip.ulRecipClass = MAPI_TO;
                    grecip.lpEntryID = 0;
                    grecip.lpszName = 0;
                    grecip.ulEIDSize = 0;
                    grecip.ulReserved = 0;
                    grecip.lpszAddress = (LPSTR)(LPCSTR)m_to.begin()->first.c_str();
                    memcpy(&pRecipients[nIndex], &grecip, sizeof(pRecipients[nIndex]));
                    nIndex++;
                }
            }
            if (!m_cc.empty())
            {
                // set cc's
                for (p = m_cc.begin(); p != m_cc.end(); ++p, ++nIndex)
                {
                    if ((bResolveNames)&&(cResolveName(hMapiSession, p->first.c_str(), &pRecip) == SUCCESS_SUCCESS))
                    {
                        if (pFirstRecipient == NULL)
                            pFirstRecipient = &pRecipients[nIndex];
                        pRecip->ulRecipClass = MAPI_CC;
                        memcpy(&pRecipients[nIndex], pRecip, sizeof pRecipients[nIndex]);
                        buffersToFree.push_back(pRecip);
                        nIndex++;
                    }
                }
            }

            if (!m_bcc.empty())
            {
                // set bcc
                for (p = m_bcc.begin(); p != m_bcc.end(); ++p, ++nIndex)
                {
                    if ((bResolveNames)&&(cResolveName(hMapiSession, p->first.c_str(), &pRecip) == SUCCESS_SUCCESS))
                    {
                        if (pFirstRecipient == NULL)
                            pFirstRecipient = &pRecipients[nIndex];
                        pRecip->ulRecipClass = MAPI_BCC;
                        memcpy(&pRecipients[nIndex], pRecip, sizeof(pRecipients[nIndex]));
                        buffersToFree.push_back(pRecip);
                        nIndex++;
                    }
                }
            }
        }
        if (pAttachments)
        {
            // add attachments
            for (p = m_attachments.begin(), nIndex = 0;
                p != m_attachments.end(); ++p, ++nIndex)
            {
                pAttachments[nIndex].ulReserved     = 0;
                pAttachments[nIndex].flFlags        = 0;
                pAttachments[nIndex].nPosition      = 0;
                pAttachments[nIndex].lpszPathName   = (LPSTR)p->first.c_str();
                pAttachments[nIndex].lpszFileName   = (LPSTR)p->second.c_str();
                pAttachments[nIndex].lpFileType     = NULL;
            }
        }

        SecureZeroMemory(&message, sizeof(message));

        if (!m_sSubject.empty())
            message.lpszSubject     = (LPSTR)m_sSubject.c_str();
        else
            message.lpszSubject = "No Subject";
        if (!m_sMessage.empty())
        {
            if (m_bHTML)
            {
                // save the message to a temp file and attach the file
                char tmpPath[MAX_PATH];
                char tmpFile[MAX_PATH];
                GetTempPathA(_countof(tmpPath), tmpPath);
                GetTempFileNameA(tmpPath, "mln", 0, tmpFile);
                HANDLE hFile = CreateFileA(tmpFile, GENERIC_WRITE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
                if (hFile != INVALID_HANDLE_VALUE)
                {
                    DWORD dwWritten = 0;
                    WriteFile(hFile, m_sMessage.c_str(), (DWORD)m_sMessage.size(), &dwWritten, NULL);
                    CloseHandle(hFile);

                    size_t nIndex = m_attachments.size();
                    pAttachments[nIndex].ulReserved     = 0;
                    pAttachments[nIndex].flFlags        = 0;
                    pAttachments[nIndex].nPosition      = -1;
                    pAttachments[nIndex].lpszPathName   = tmpFile;
                    pAttachments[nIndex].lpszFileName   = "default.htm";
                    pAttachments[nIndex].lpFileType     = NULL;

                    message.lpszNoteText    = NULL;
                }
                else
                    message.lpszNoteText    = (LPSTR)m_sMessage.c_str();
            }
            else
                message.lpszNoteText        = (LPSTR)m_sMessage.c_str();
        }
        else
            message.lpszNoteText = "No Message Body";
        message.lpszMessageType     = NULL;
        message.lpszDateReceived    = NULL;
        message.lpszConversationID  = NULL;
        message.flFlags             = 0;
        message.lpOriginator        = pOriginator;
        message.nRecipCount         = nIndex;
        message.lpRecips            = pFirstRecipient;
        message.nFileCount          = nAttachments;
        message.lpFiles             = pAttachments;

        status = m_lpMapiSendMail(hMapiSession, 0, &message, MAPI_DIALOG, 0);

        m_lpMapiLogoff(hMapiSession, NULL, 0, 0);
        for (auto iter = buffersToFree.begin(); iter != buffersToFree.end(); ++iter)
        {
            m_lpMapiFreeBuffer(*iter);
        }

        if (pRecipients)
            delete [] pRecipients;

        if (pAttachments)
            delete [] pAttachments;
    }

    if (SUCCESS_SUCCESS == status)
        return 1;
    if (MAPI_E_USER_ABORT == status)
        return -1;
    // other failure
    return 0;
}

BOOL CMailMsg::Initialize()
{
    m_hMapi = ::LoadLibrary(_T("mapi32.dll"));

    if (!m_hMapi)
        return FALSE;

    m_lpMapiLogon = (LPMAPILOGON)::GetProcAddress(m_hMapi, ("MAPILogon"));
    m_lpMapiSendMail = (LPMAPISENDMAIL)::GetProcAddress(m_hMapi, ("MAPISendMail"));
    m_lpMapiLogoff = (LPMAPILOGOFF)::GetProcAddress(m_hMapi, ("MAPILogoff"));
    m_lpMapiResolveName = (LPMAPIRESOLVENAME) GetProcAddress(m_hMapi, ("MAPIResolveName"));
    m_lpMapiFreeBuffer = (LPMAPIFREEBUFFER) GetProcAddress(m_hMapi, ("MAPIFreeBuffer"));

    m_bReady = (m_lpMapiLogon && m_lpMapiSendMail && m_lpMapiLogoff);

    return m_bReady;
}

void CMailMsg::Uninitialize()
{
    ::FreeLibrary(m_hMapi);
}
